/*
 * @file iso9660.c
 *
 *   ISO9660 block device I/O interface
 */
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/bdbuf.h>
#include <rtems/seterr.h>

#include "iso9660.h"
#include "iso9660_io.h"


int
iso9660_buffer_open(
    struct stat       *host_volume,
    iso9660_fs_info_t  *fs
)
{
  /* Source volume is a block device */
  rtems_disk_device     *iso9660_volume;

  /* Acquire the volume */
  iso9660_volume = rtems_disk_obtain(host_volume->st_rdev);
  if (iso9660_volume == NULL)
    rtems_iso9660_error_and_return_minus_one(EIO,
        "buffer_open: failed to obtain disk.");

  fs->disk = iso9660_volume;
  fs->dev = host_volume->st_rdev;
  fs->type = ISO9660_VOLUME_TYPE_BLOCK;

  return 0;
}


/*
 *   Retrieve a block from the source volume into ISO9660 cache through
 * block device interface.
 *
 * @param vol_info  Pointer to the ISO9660 volume information descriptor
 * @param block     Block number
 * @param buffer    Destination buffer
 *
 * @return 0 if everything OK, else -1
 */
int
iso9660_buffer_acquire(
    iso9660_fs_info_t      *fs,
    uint32_t                block,
    rtems_bdbuf_buffer    **buffer
)
{
  rtems_status_code rc;

  /* The block cache is empty, let use it */
  if (fs->cache.buffer == NULL) {
    rc = rtems_bdbuf_read(rtems_iso9660_device(fs), block, &(fs->cache.buffer));
    if (rc != RTEMS_SUCCESSFUL) {
      fs->cache.buffer = NULL;
      rtems_iso9660_error_and_return_minus_one(EIO,
          "buffer_acquire: failed to read from block device.");
    }

    fs->cache.block = block;
  }

  /* We need to retrieve the good block */
  if ((fs->cache.block != block) || (fs->cache.buffer == NULL)) {
    if (fs->cache.buffer != NULL)
    {
      /* Release the previous cache */
      rc = rtems_bdbuf_release(fs->cache.buffer);
      fs->cache.buffer = NULL;
      if (rc != RTEMS_SUCCESSFUL)
        rtems_iso9660_error_and_return_minus_one(EIO,
            "buffer_acquire: failed to release block device buffer.");
    }

    /* Cache the new block */
    rc = rtems_bdbuf_read(rtems_iso9660_device(fs), block, &fs->cache.buffer);
    if (rc != RTEMS_SUCCESSFUL) {
      printf("rc=%d\n", rc);
      fs->cache.buffer = NULL;
      rtems_iso9660_error_and_return_minus_one(EIO,
          "buffer_acquire: failed to read from block device.");
    }

    fs->cache.block = block;
  }

  *buffer = fs->cache.buffer;

  return rc;
}



/*
 *   Release a previously acquired buffer.
 *
 * @param buffer    Acquired buffer
 *
 * @return 0 if everything OK, else -1
 */
int
iso9660_buffer_release(
    iso9660_fs_info_t  *fs,
    rtems_bdbuf_buffer *buffer
)
{
  rtems_status_code rc = 0;

  /* The buffer is in the cache, clear the cache */
  if (fs->cache.buffer == buffer) {
    /* Release the previous cache */
    rc = rtems_bdbuf_release(fs->cache.buffer);
    fs->cache.buffer = NULL;
    if (rc != RTEMS_SUCCESSFUL)
      rtems_iso9660_error_and_return_minus_one(EIO,
          "buffer_acquire: failed to release block device buffer.");
  }

  return rc;
}
